package com.partsoft.dits.ldap;

import java.sql.Types;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.partsoft.dits.MonitorIntegrateDescriptor;
import com.partsoft.dits.MonitorStartMode;
import com.partsoft.dits.message.DataAction;
import com.partsoft.dits.message.DataProperty;

/**
 * LDAP变更监听描述符
 */
public class LdapChangeLogMonitorDescriptor implements MonitorIntegrateDescriptor, Cloneable, BeanNameAware {

	private String id;

	private String ldapContext;

	private String title;

	private String description;

	private MonitorStartMode startMode = MonitorStartMode.AUTO;

	private long monitorInterval = DEFAULT_MONITOR_INTERVAL;
	
	private int retrieveCount = DEFAULT_RETRIEVE_COUNT;

	/**
	 * 变更记录父亲节点DN
	 */
	private String changeLogParentDN = "cn=changelog";

	/**
	 * 变更记录LDAP类
	 */
	private String changeLogObjectClass = "changelogentry";

	/**
	 * 变更记录实体ID
	 */
	private String changeLogEntityID = "changenumber";
	
	/**
	 * 变更的内容属性名
	 */
	private String changedContentPropertyName = "changes";
	
	/**
	 * 变更的内容的分隔
	 */
	private String changedContextPropertySplit = "\n";
	

	/**
	 * 目标变更属性
	 */
	private String targetEntityPropertyName = "targetdn";

	/**
	 * 变更属性
	 */
	private String changeTypePropertyName = "changetype";

	/**
	 * 目标变更属性过滤器
	 */
	private String targetEntityPropertyFilter;

	private List<DataProperty> changeLogProperties = null;

	private Map<String, DataAction> changeTypeMaps = null;
	
	private int ordinal = 0;
	
	private static Map<String, DataAction> DefaultChangeTypeMaps = new HashMap<String, DataAction>(3);

	static {
		DefaultChangeTypeMaps.put("add", DataAction.INSERT);
		DefaultChangeTypeMaps.put("modify", DataAction.UPDATE);
		DefaultChangeTypeMaps.put("delete", DataAction.DELETE);
	}

	public LdapChangeLogMonitorDescriptor() {
		setChangeLogPropertyNames("changenumber=-5;targetdn=12;changetype=12;changes=12");
	}

	public LdapChangeLogMonitorDescriptor(String ldapContext) {
		setLdapContext(ldapContext);
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public MonitorStartMode getStartMode() {
		return startMode;
	}

	public void setStartMode(MonitorStartMode startMode) {
		this.startMode = startMode;
	}

	public long getMonitorInterval() {
		return monitorInterval;
	}

	public void setMonitorInterval(long monitorInterval) {
		this.monitorInterval = monitorInterval;
	}
	
	public int getOrdinal() {
		return ordinal;
	}

	public void setOrdinal(int ordinal) {
		this.ordinal = ordinal;
	}

	public void setBeanName(String arg0) {
		setId(arg0);
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

	public String getChangedContentPropertyName() {
		return changedContentPropertyName;
	}

	public void setChangedContentPropertyName(String changedContentPropertyName) {
		this.changedContentPropertyName = changedContentPropertyName;
	}

	public String getLdapContext() {
		return ldapContext;
	}

	public void setLdapContext(String ldapContext) {
		this.ldapContext = ldapContext;
	}

	public String getChangeLogParentDN() {
		return changeLogParentDN;
	}

	public void setChangeLogParentDN(String changeLogParentDN) {
		this.changeLogParentDN = changeLogParentDN;
	}

	public String getChangeLogObjectClass() {
		return changeLogObjectClass;
	}

	public void setChangeLogObjectClass(String changeLogObjectClass) {
		this.changeLogObjectClass = changeLogObjectClass;
	}

	public String getChangeLogEntityID() {
		return changeLogEntityID;
	}

	public void setChangeLogEntityID(String changeLogEntityID) {
		this.changeLogEntityID = changeLogEntityID;
	}

	public String getTargetEntityPropertyName() {
		return targetEntityPropertyName;
	}

	public void setTargetEntityPropertyName(String targetEntityPropertyName) {
		this.targetEntityPropertyName = targetEntityPropertyName;
	}

	public String getTargetEntityPropertyFilter() {
		return targetEntityPropertyFilter;
	}

	public void setTargetEntityPropertyFilter(String targetEntityPropertyFilter) {
		this.targetEntityPropertyFilter = targetEntityPropertyFilter;
	}

	public int getRetrieveCount() {
		return retrieveCount;
	}

	public void setRetrieveCount(int retrieveCount) {
		this.retrieveCount = retrieveCount;
	}

	@SuppressWarnings("unchecked")
	public List<DataProperty> getChangeLogProperties() {
		return changeLogProperties == null ? Collections.EMPTY_LIST : Collections.unmodifiableList(changeLogProperties);
	}
	
	public Set<String> getChangeLogPropertyNameSet() {
		Set<String> propsNames = new LinkedHashSet<String>();
		for (DataProperty prop : getChangeLogProperties()) {
			propsNames.add(prop.getPropertyName());
		}
		return new HashSet<String>(propsNames);
	}

	public void setChangeLogProperties(List<DataProperty> changeLogProperties) {
		this.changeLogProperties = changeLogProperties;
	}

	public String getChangeLogPropertyNames() {
		StringBuffer buffer = new StringBuffer();
		boolean append = false;
		for (DataProperty cln : getChangeLogProperties()) {
			if (append)
				buffer.append(";");
			append = true;
			buffer.append(cln.getPropertyName()).append("=").append(cln.getValueType());
		}
		return buffer.toString();
	}

	public Map<String, DataAction> getChangeTypeMaps() {
		return changeTypeMaps != null ? Collections.unmodifiableMap(changeTypeMaps) : DefaultChangeTypeMaps;
	}

	public void setChangeTypeMaps(Map<String, DataAction> changeTypeMaps) {
		this.changeTypeMaps = new HashMap<String, DataAction>(changeTypeMaps);
		if (this.changeTypeMaps.size() == 0) {
			this.changeTypeMaps = null;
		}
	}

	public DataAction getDataActionByNameMap(String name) {
		DataAction result = this.getChangeTypeMaps().get(name);
		Assert.notNull(result);
		return result;
	}

	public void addChangeLogProperty(DataProperty entityProperty) {
		Assert.notNull(entityProperty);
		if (changeLogProperties == null) {
			changeLogProperties = new LinkedList<DataProperty>();
			changeLogProperties.add(entityProperty);
		} else if (!changeLogProperties.contains(entityProperty)) {
			changeLogProperties.add(entityProperty);
		}
	}

	public void setChangeLogPropertyNames(String propertyNames) {
		Assert.hasText(propertyNames, "必须设置实体属性");
		String[] keys = propertyNames.split(";");
		Properties nameProps = StringUtils.splitArrayElementsIntoProperties(keys, "=");
		for (Object oc : nameProps.keySet()) {
			String clnName = oc.toString();
			int keyType = Types.VARCHAR;
			if (StringUtils.hasText(nameProps.getProperty(clnName)))
				try {
					keyType = Integer.parseInt(nameProps.getProperty(clnName));
				} catch (NumberFormatException e) {
				}
			addChangeLogProperty(new DataProperty(clnName, keyType));
		}
	}

	public String getChangeTypePropertyName() {
		return changeTypePropertyName;
	}

	public void setChangeTypePropertyName(String changeTypePropertyName) {
		this.changeTypePropertyName = changeTypePropertyName;
	}
	
	public void setChangeTypeMapNames(String propertyNames) {
		Assert.hasText(propertyNames, "必须设置实体属性");
		String[] keys = propertyNames.split(";");
		Properties nameProps = StringUtils.splitArrayElementsIntoProperties(keys, "=");
		changeTypeMaps = new HashMap<String, DataAction>(nameProps.size()); 
		for (Object oc : nameProps.keySet()) {
			String clnName = oc.toString();
			DataAction act = DataAction.fromName(nameProps.getProperty(clnName));
			Assert.notNull(act);
			changeTypeMaps.put(clnName, act);
		}
	}
	
	public String getChangeTypeMapNames() {
		StringBuffer buffer = new StringBuffer();
		boolean append = false;
		for (Map.Entry<String, DataAction> cln : getChangeTypeMaps().entrySet()) {
			if (append)
				buffer.append(";");
			append = true;
			buffer.append(cln.getKey()).append("=").append(cln.getValue().name());
		}
		return buffer.toString();
	}

	@Override
	public LdapChangeLogMonitorDescriptor clone() {
		try {
			return (LdapChangeLogMonitorDescriptor) super.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
	}
	
	public String getChangedContextPropertySplit() {
		return changedContextPropertySplit;
	}

	public void setChangedContextPropertySplit(String changedContextPropertySplit) {
		this.changedContextPropertySplit = changedContextPropertySplit;
	}

	@Override
	public String toString() {
		return "LDAP监听描述符[LDAP上下文ID为" + getLdapContext() + "]";
	}

}
